/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com> 
 *
 */
#ifndef SRC_PROTOCOLS_QUIC_QUICPROTOCOL_H_
#define SRC_PROTOCOLS_QUIC_QUICPROTOCOL_H_

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <arpa/inet.h>

#include "Protocol.h"
#include "Cache.h"
#include "QuicInfo.h"
#include "flow/FlowManager.h"

namespace aiengine {

struct quic_header {
	uint8_t 	flags;
	uint64_t 	cid;
	uint32_t 	version;
	uint8_t 	pkt_number;
	uint8_t 	data[0];
} __attribute__((packed));

struct quic_frame {
	uint8_t 	type;
	uint8_t		id;
	uint16_t	length;
	uint8_t		tag[4];
	uint16_t	tags;
	uint8_t		pad;
	uint8_t		data[0];
} __attribute__((packed));

struct quic_tag_value {
	uint8_t		tag[4];
	uint32_t	length;
	uint8_t		data[0];
} __attribute__((packed));

class QuicProtocol: public Protocol {
public:
    	explicit QuicProtocol();
    	virtual ~QuicProtocol() {}

	static constexpr uint16_t header_size = sizeof(quic_header);

	uint16_t getId() const override { return 0x0000; }
	uint16_t getHeaderSize() const override { return header_size;}

	// Condition for say that a packet is quic
	bool check(const Packet &packet) override;
        void processFlow(Flow *flow) override;
        bool processPacket(Packet &packet) override { return true; } 

	void statistics(std::basic_ostream<char> &out, int level, int32_t limit) const override;
	void statistics(Json &out, int level) const override;
	void statistics(Json &out, const std::string &map_name, int32_t limit) const override;

	void releaseCache() override; 

	void setHeader(const uint8_t *raw_packet) override { 

		header_ = reinterpret_cast <const quic_header*> (raw_packet);
	}

        void increaseAllocatedMemory(int value) override;
        void decreaseAllocatedMemory(int value) override;

        void setDomainNameManager(const SharedPointer<DomainNameManager> &dm) override;

        void setFlowManager(FlowManagerPtrWeak flow_mng) { flow_mng_ = flow_mng; }
	
	uint64_t getCurrentUseMemory() const override;
	uint64_t getAllocatedMemory() const override;
	uint64_t getTotalAllocatedMemory() const override; 

        void setDynamicAllocatedMemory(bool value) override; 
        bool isDynamicAllocatedMemory() const override; 

	CounterMap getCounters() const override; 
	void resetCounters() override;

	uint32_t getTotalEvents() const override { return total_events_; }
	uint32_t getTotalCacheMisses() const override;

        void releaseFlowInfo(Flow *flow) override;

        Flow* getCurrentFlow() const { return current_flow_; }

private:
	void handle_client_hello(QuicInfo *info, const uint8_t *data, int length, int tags);
        void attach_host_name(QuicInfo *info, const boost::string_ref &name);
        void attach_user_agent(QuicInfo *info, const boost::string_ref &name);
        uint64_t compute_memory_used_by_maps() const;

	uint32_t total_events_ = 0;

        Cache<QuicInfo>::CachePtr info_cache_ = Cache<QuicInfo>::CachePtr(new Cache<QuicInfo>("Quic Info cache"));
        Cache<StringCache>::CachePtr host_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("Host cache"));
        Cache<StringCache>::CachePtr ua_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("UserAgent cache"));

        StringMap host_map_ {"Host names", "Host"};
        StringMap ua_map_ {"UserAgent names", "UserAgent"};

        FlowManagerPtrWeak flow_mng_ = FlowManagerPtrWeak();
        Flow *current_flow_ = nullptr;

        SharedPointer<DomainNameManager> domain_mng_ = nullptr;

	const quic_header *header_ = nullptr;
};

typedef std::shared_ptr<QuicProtocol> QuicProtocolPtr;

} // namespace aiengine

#endif  // SRC_PROTOCOLS_QUIC_QUICPROTOCOL_H_
